
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.

# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)

# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
set(MY_PROJECT "OllvmTest")

project(${MY_PROJECT})

# 打印项目根目录路径
message(STATUS "Project root directory: ${PROJECT_SOURCE_DIR}")

# 指定 Clang 编译器
set(CMAKE_C_COMPILER "clang")
set(CMAKE_CXX_COMPILER "clang++")

# 可选：检查编译器是否成功设置
if (NOT CMAKE_C_COMPILER MATCHES "clang" OR NOT CMAKE_CXX_COMPILER MATCHES "clang\\+\\+")
    message(FATAL_ERROR "Clang compiler is required!")
endif()

# 查找自己编译的LLVM的链接器
find_program(LLD_PATH ld64.lld)
if(LLD_PATH)
    message(STATUS "Using LLD linker: ${LLD_PATH}")
else()
    message(FATAL_ERROR "LLD not found, falling back to default linker")
endif()

set(INCLUDE_DIRS
        ${PROJECT_SOURCE_DIR}
)

# 包含头文件目录
include_directories(${INCLUDE_DIRS})

# 设置C++标准（可选）
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)

# 定义源码目录
set(SOURCE_DIR "${PROJECT_SOURCE_DIR}")

# 查找所有的 .cpp 文件
file(GLOB SOURCES
    "${SOURCE_DIR}/*.cpp"
)

message(STATUS "SOURCES: ${SOURCES}")

set(MY_BUILD_ALL "-DMY_BUILD_ALL")

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MY_BUILD_ALL}")

# 间接跳转,并加密跳转目标。处理代码在 IndirectBranch.cpp
set(MY_LLVM_IROBF_INDBR "-mllvm -irobf-indbr")
string(REPLACE " " ";" MY_LLVM_IROBF_INDBR_LL "${MY_LLVM_IROBF_INDBR}")  # 替换空格为分号

# 间接函数调用,并加密目标函数地址。处理代码在 IndirectCall.cpp
set(MY_LLVM_IROBF_ICALL "-mllvm -irobf-icall")
string(REPLACE " " ";" MY_LLVM_IROBF_ICALL_LL "${MY_LLVM_IROBF_ICALL}")  # 替换空格为分号

# 字符串(c string)加密功能。处理代码在 StringEncryption.cpp
set(MY_LLVM_IROBF_CSE "-mllvm -irobf-cse")
string(REPLACE " " ";" MY_LLVM_IROBF_CSE_LL "${MY_LLVM_IROBF_CSE}")  # 替换空格为分号

# 整数常量加密
set(MY_LLVM_IROBF_CIE "-mllvm -irobf-cie")
string(REPLACE " " ";" MY_LLVM_IROBF_CIE_LL "${MY_LLVM_IROBF_CIE}")  # 替换空格为分号

# 过程相关控制流平坦混淆。处理代码在 Flattening.cpp 和 LegacyLowerSwitch.cpp
set(MY_LLVM_IROBF_CFF "-mllvm -irobf-cff")
string(REPLACE " " ";" MY_LLVM_IROBF_CFF_LL "${MY_LLVM_IROBF_CFF}")  # 替换空格为分号

# 添加 Clang/LLVM 特定参数（如控制流混淆）
add_compile_options(
    "SHELL:${MY_LLVM_IROBF_INDBR}"  # 间接跳转,并加密跳转目标
    "SHELL:${MY_LLVM_IROBF_ICALL}"  # 间接函数调用,并加密目标函数地址
    # "SHELL:-mllvm -irobf-indgv"   # 间接全局变量引用,并加密变量地址
    "SHELL:${MY_LLVM_IROBF_CSE}"    # 字符串(c string)加密功能
    "SHELL:${MY_LLVM_IROBF_CFF}"    # 过程相关控制流平坦混淆
    "SHELL:${MY_LLVM_IROBF_CIE}"    # 整数常量加密
    # "SHELL:-mllvm -irobf-cfe"     # 浮点常量加密
)


# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_executable(${MY_PROJECT} ${SOURCES})

target_link_options(${MY_PROJECT} PRIVATE 
    -fuse-ld=${LLD_PATH}
    -v
)


# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.
# target_link_libraries(${CMAKE_PROJECT_NAME}
#     # List libraries link to the target library
#     android
#     log)


message(STATUS "CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}")

set(CMAKE_OSX_SYSROOT_WRAPPER "")
if(APPLE)
    # 执行 xcrun --show-sdk-path 并存储结果到 CMAKE_OSX_SYSROOT
    execute_process(
        COMMAND xcrun --show-sdk-path
        OUTPUT_VARIABLE CMAKE_OSX_SYSROOT
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )

    message(STATUS "Detected macOS SDK path: ${CMAKE_OSX_SYSROOT}")

    set(CMAKE_OSX_SYSROOT_WRAPPER "-isysroot ${CMAKE_OSX_SYSROOT}")

    # 设置编译器标志（可选，CMake 通常会自动处理）
    if(CMAKE_OSX_SYSROOT)
        set(CMAKE_OSX_SYSROOT_ARG "-isysroot")
    #     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isysroot ${CMAKE_OSX_SYSROOT}")
    #     set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -isysroot ${CMAKE_OSX_SYSROOT}")
    endif()
endif()

message(STATUS "CMAKE_OSX_SYSROOT_WRAPPER: ${CMAKE_OSX_SYSROOT_WRAPPER}")

# 为每个源文件生成LLVM IR，输出IR用于查看中间结果，下面的-mllvm的参数需要手动和可执行文件保持一致
foreach(src_file ${SOURCES})
    # 提取不带扩展名的文件名（main/utils）
    get_filename_component(src_name ${src_file} NAME_WE)
    message(STATUS "src_file: ${src_file},src_name:${src_name}")
    set(ll_file_name "${CMAKE_CURRENT_BINARY_DIR}/${src_name}.ll")

    add_custom_command(
        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${src_name}.ll
        COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_OSX_SYSROOT_ARG} ${CMAKE_OSX_SYSROOT} ${MY_BUILD_ALL} ${MY_LLVM_IROBF_INDBR_LL} ${MY_LLVM_IROBF_ICALL_LL} ${MY_LLVM_IROBF_CSE_LL} ${MY_LLVM_IROBF_CFF_LL} ${MY_LLVM_IROBF_CIE_LL} -S -emit-llvm ${src_file} -o ${ll_file_name}
        DEPENDS ${src_file}
        COMMENT "IR --> ${ll_file_name}"
    )

    add_custom_target(${src_name}_ll DEPENDS ${ll_file_name})
    add_dependencies(${MY_PROJECT} ${src_name}_ll)
endforeach()